package edu.neu.swc.commerce.service.communication;

import com.alibaba.fastjson.JSON;
import com.netflix.loadbalancer.*;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import edu.neu.swc.commerce.constant.CommonConstant;
import edu.neu.swc.commerce.vo.JwtToken;
import edu.neu.swc.commerce.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import rx.Observable;

import java.util.ArrayList;
import java.util.List;

/**
 * <h1>使用Ribbon实现微服务通信</h1>
 */
@Slf4j
@Service
public class UseRibbonService {

    private final RestTemplate restTemplate;

    private final DiscoveryClient discoveryClient;

    public UseRibbonService(RestTemplate restTemplate, DiscoveryClient discoveryClient) {
        this.restTemplate = restTemplate;
        this.discoveryClient = discoveryClient;
    }

    /**
     * <h2>通过Ribbon调用Authority服务去获取Token</h2>
     */
    public JwtToken getTokenFromAuthorityServiceByRibbon(
            UsernameAndPassword usernameAndPassword
    ){
        //注意到URL中的IP和端口号换成了服务名称
        String requestUrl = String.format(
                "http://%s/ecommerce-authority-center/authority/token",
                CommonConstant.AUTHORITY_CENTER_SERVICE_ID
        );

        log.info("login request url and Body:[{}],[{}]",requestUrl,
                JSON.toJSONString(usernameAndPassword));

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(MediaType.APPLICATION_JSON);

        //这里一定要使用注入的restTemplate
        return restTemplate.postForObject(
                requestUrl,
                new HttpEntity<>(JSON.toJSONString(usernameAndPassword),headers),
                JwtToken.class
        );
    }

    /**
     * <h2>使用原生的 Ribbon Api 看看ribbon的思想</h2>
     */
    public JwtToken thinkingInRibbon(UsernameAndPassword usernameAndPassword){
        String urlFormat = "http://%s/ecommerce-authority-center/authority/token";

        //1.找到服务提供方的IP和端口号
        List<ServiceInstance> targetInstances = discoveryClient.getInstances(
                CommonConstant.AUTHORITY_CENTER_SERVICE_ID
        );

        //2.构建Ribbon需要的服务列表
        List<Server> servers = new ArrayList<>(targetInstances.size());
        targetInstances.forEach(i -> {
            servers.add(new Server(i.getHost(), i.getPort()));
            log.info("found target instance:[{}] -> [{}]", i.getHost(),i.getPort());
        });

        //使用负载均衡策略实现远端调用
        BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder()
                .buildFixedServerListLoadBalancer(servers);

        //设置负载均衡策略
        loadBalancer.setRule(new RetryRule(new RandomRule(),300));

        String result = LoadBalancerCommand.builder()
                .withLoadBalancer(loadBalancer)
                .build()
                .submit(server -> {
                    String targetUrl = String.format(
                            urlFormat,
                            String.format("%s:%s",server.getHost(),server.getPort())
                    );

                    log.info("target requestUrl:[{}]",targetUrl);

                    HttpHeaders headers = new HttpHeaders();

                    headers.setContentType(MediaType.APPLICATION_JSON);

                    String tokenStr = new RestTemplate().postForObject(
                            targetUrl,
                            new HttpEntity<>(
                                    JSON.toJSONString(usernameAndPassword),
                                    headers),
                            String.class
                    );

                    return Observable.just(tokenStr);
                })
                .toBlocking()
                .first()
                .toString();

        return JSON.parseObject(result,JwtToken.class);
    }
}
